package br.ufrgs.meetapp.services;

import java.util.ArrayList;

import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
import br.ufrgs.meetapp.enums.SettingsComponent;
import br.ufrgs.meetapp.models.MFTSettings;

public class LocationService extends Service {

	/**
	 * @uml.property  name="mINIMUM_DISTANCE_BETWEEN_UPDATES"
	 */
	private final long MINIMUM_DISTANCE_BETWEEN_UPDATES = 0; // in Meters
	/**
	 * @uml.property  name="mINIMUM_TIME_BETWEEN_UPDATES"
	 */
	private final long MINIMUM_TIME_BETWEEN_UPDATES = 1000; // in
	
	/**
	 * Keeps track of all current registered clients.
	 * @uml.property  name="serviceClients"
	 */
    private ArrayList<Messenger> serviceClients = new ArrayList<Messenger>();
	
	
    /**
	 * Target we publish for clients to send messages to Outgoing.
	 * @uml.property  name="messenger"
	 * @uml.associationEnd  multiplicity="(1 1)"
	 */
	private final Messenger messenger = new Messenger(new IncomingMessageHandler());

	
	private static TrackerLocationListener locationListener;
	private static TrackerGPSStatusListener statusListener;
	
	private Context context;
	
	// Acquire a reference to the system Location Manager
	private static LocationManager locationManager;
	
	private MFTSettings settings;
	
    @Override
	public void onCreate() {
		//Toast.makeText(this, "Service Created", Toast.LENGTH_LONG).show();
    	settings = new MFTSettings(this);
		log("onCreate");
	}
    
	@Override
	public IBinder onBind(Intent arg0) {
		
		startLocationManager();
		
		return messenger.getBinder();
	}

	private void startLocationManager() {
		context = this;
		settings.refresh();
		if(locationManager == null) {
			locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
			startAcquiringPosition();
		}
	}
	
	@Override
	public boolean onUnbind(Intent intent) {
		stopAcquiringPosition();
		if(serviceClients != null)
			log("Clients: " + serviceClients.size());
		else 
			log("Clients array is null");
		return false;
	}
	
	/**
	 * Stops to listen the GPS position and status events
	 */
	private void stopAcquiringPosition() {
		try {
			log("Stopping the location service");
			if (locationManager != null) {
				locationManager.removeUpdates(locationListener);
				locationManager.removeGpsStatusListener(statusListener);
				
				// TODO: check this line, I believe that the location manager is never turned on again if we don't set it to null here
				locationManager = null;
			}
			
		} catch (Exception exception) {
			System.out.println("Error in LocationService.stopAcquiringPosition - trying to create new listeners or use them");
			if(exception.getMessage() != null)
				System.out.println(exception.getMessage());
			exception.printStackTrace();
		}
	}
	
	/**
	 * Starts to listen the GPS position and status events
	 */
	private void startAcquiringPosition() {
		try {
			log("Setting up the listener");
			
			locationListener = new TrackerLocationListener();
			// Register the listener with the Location Manager to receive
			// location updates
			locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MINIMUM_TIME_BETWEEN_UPDATES, MINIMUM_DISTANCE_BETWEEN_UPDATES, locationListener);
			locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MINIMUM_TIME_BETWEEN_UPDATES, MINIMUM_DISTANCE_BETWEEN_UPDATES, locationListener);
			log("Listener set");
			
		} catch (Exception exception) {
			System.out.println("Error in LocationService.startAcquiringPosition - trying to create new listeners or use them");
			if(exception.getMessage() != null)
				System.out.println(exception.getMessage());
			exception.printStackTrace();
		}
	}
		
	/**
	 * Easily log messages
	 * @param message - message that will be displayed
	 */
	private void log(String message) {
		Log.i("MFT - LocationService", message);
	}
	
	/**
	 * Listen to Location changes
	 * @author fernandoheck
	 *
	 */
	private class TrackerLocationListener implements LocationListener {

		public void onProviderEnabled(String provider) {
			log("Provider Enabled");
		}

		public void onProviderDisabled(String provider) {
			log("Provider disabled");
		}      

		@Override
		public void onLocationChanged(Location location) {
			
			log("Position Changed: "+location.getLatitude()+" "+location.getLongitude());
			
			verifyMovement(location);	
			
			for (Messenger messenger : serviceClients) {
				try {
//					messenger.send(Message.obtain(null, GenericService.MESSAGE_NEW_LOCATION, location));					
				} catch (Exception exception) {
					System.out.println("Error in LocationService -> TrackerLocationListener.onLocationChanged - trying to send new location to client: " + messenger.getBinder().toString());
					if(exception.getMessage() != null)
						System.out.println(exception.getMessage());
					exception.printStackTrace();
				}
			}
		}
		
		/***
		 * Verify if device is moving, if so call PositionService
		 * @param location - used to define if device is moving or not
		 */
		private void verifyMovement(Location location) {
			int minSpeed = (Integer)settings.getValue(SettingsComponent.MOTION_SPEED_THRESHOLD);
			if (location.getSpeed()*3.6 > minSpeed){
				
				if (!isPositionServiceRunning()){
					
					Log.i("PositionService", "Got Speed "+String.valueOf(location.getSpeed()*3.6)+"Km/h > "+minSpeed+"Km/h: start PositionService");
					
//					Intent startIntent = new Intent().setClass(context, PositionService.class);
//					context.startService(startIntent);
				}
			} 
		}
		
		 /**
		 * Checks whether the Position Service is running
		 * 
		 * @return
		 */
		private boolean isPositionServiceRunning() {
			ActivityManager manager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
			for (RunningServiceInfo service : manager
					.getRunningServices(Integer.MAX_VALUE)) {
				if ("com.mecomo.mft.services.PositionService".equals(service.service.getClassName())) {
					return true;
				}
			}
			return false;
		}
		
		@Override
		public void onStatusChanged(String provider, int status, Bundle extras) {
			
			for(Messenger messenger : serviceClients) {
				try {
//					messenger.send(Message.obtain(null, GenericService.MESSAGE_LOCATION_STATUS_CHANGED, status));
				} catch (Exception exception) {
					System.out.println("Error in LocationService -> TrackerLocationListener.onStatusChanged - trying to send new location status to client: " + messenger.getBinder().toString());
					if(exception.getMessage() != null)
						System.out.println(exception.getMessage());
					exception.printStackTrace();
				}
			}
			
			log("Status changed");
		}
	}

	/**
	 * Handle GPS status changed
	 * @author fernandoheck
	 *
	 */
	private class TrackerGPSStatusListener implements GpsStatus.Listener {

		@Override
		public void onGpsStatusChanged(int event) {
			log("Status changed\nEvent: " + event);
			
			for(Messenger messenger : serviceClients) {
				try {
//					messenger.send(Message.obtain(null, GenericService.MESSAGE_GPS_STATUS_CHANGED, locationManager.getGpsStatus(null)));
				} catch (Exception exception) {
					System.out.println("Error in LocationService -> TrackerLocationListener.onStatusChanged - trying to send new location status to client: " + messenger.getBinder().toString());
					if(exception.getMessage() != null)
						System.out.println(exception.getMessage());
					exception.printStackTrace();
				}
			}
		}
	}

	/**
     * Handler of incoming messages from clients.
     */
    private class IncomingMessageHandler extends Handler {
        @Override
        public void handleMessage(Message message) {
        	
            switch (message.what) {
//                case GenericService.MESSAGE_REGISTER_CLIENT:
//                	serviceClients.add(message.replyTo);
//                	log("New client binded - Clients: " + serviceClients.size());
//                    break;
//                case GenericService.MESSAGE_UNREGISTER_CLIENT:
//                	serviceClients.remove(message.replyTo);
//                	log("Some client unbinded - Clients: " + serviceClients.size());
//                    break;
                default:
                    super.handleMessage(message);
            }
        }
    }
}
